package com.hzy.admin.services.system.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzy.admin.models.vo.system.*;
import com.hzy.admin.configs.AppConfigs;
import com.hzy.admin.services.system.*;
import com.hzy.admin.repository.mapper.system.SysMenuMapper;
import com.hzy.admin.repository.mapper.system.SysRoleMenuFunctionMapper;
import com.hzy.admin.repository.entity.system.SysFunction;
import com.hzy.admin.repository.entity.system.SysMenu;
import com.hzy.admin.repository.entity.system.SysMenuFunction;
import com.hzy.admin.repository.entity.system.SysRoleMenuFunction;
import com.hzy.toolkit.Tools;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hzy
 * @since 2020-04-28
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    private final SysRoleMenuFunctionMapper sysRoleMenuFunctionMapper;
    private final AppConfigs appConfigs;
    private final ISysUserService sysUserService;
    private final ISysFunctionService sysFunctionService;
    private final ISysMenuFunctionService sysMenuFunctionService;
    // 解决循环依赖问题
    @Autowired
    private ISysRoleMenuFunctionService sysRoleMenuFunctionService;

    public SysMenuServiceImpl(SysRoleMenuFunctionMapper sysRoleMenuFunctionMapper, AppConfigs appConfigs,
                              ISysUserService sysUserService, ISysFunctionService sysFunctionService,
                              ISysMenuFunctionService sysMenuFunctionService) {
        this.sysRoleMenuFunctionMapper = sysRoleMenuFunctionMapper;
        this.appConfigs = appConfigs;
        this.sysUserService = sysUserService;
        this.sysFunctionService = sysFunctionService;
        this.sysMenuFunctionService = sysMenuFunctionService;
    }

    @Override
    public TableVo<Map<String, Object>> findList(Integer page, Integer size, SysMenu search) {
        Page<Map<String, Object>> iPage = new Page<>(page, size);
        List<Map<String, Object>> data = this.baseMapper.getList(iPage, search);
        return TableVo.page(iPage, data);
    }

    @Override
    public Map<String, Object> findById(String id) {
        Map<String, Object> map = new HashMap<>();

        SysMenu model = this.baseMapper.selectById(id);
        List<SysFunction> allFunctions = this.sysFunctionService.lambdaQuery().orderByAsc(SysFunction::getNum).list();
        Stream<String> functionIds = this.sysMenuFunctionService.lambdaQuery().eq(SysMenuFunction::getMenuId, id)
                .select(SysMenuFunction::getFunctionId).list().stream().map(SysMenuFunction::getFunctionId);

        map.put("id", id);
        map.put("model", Tools.nullSafe(model, new SysMenu()));
        map.put("allFunctions", allFunctions);
        map.put("functionIds", functionIds);
        return map;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String insertOrUpdate(SysMenuVo dto) {
        SysMenu model = dto.getModel();
        List<String> functionIds = dto.getFunctionIds();

        this.saveOrUpdate(model);

        List<SysMenuFunction> menuFunctions = this.sysMenuFunctionService.lambdaQuery().eq(SysMenuFunction::getMenuId, model.getId())
                .list();
        this.sysMenuFunctionService
                .remove(new LambdaQueryWrapper<SysMenuFunction>().eq(SysMenuFunction::getMenuId, model.getId()));
        if (!functionIds.isEmpty()) {
            for (String functionId : functionIds) {
                Optional<SysMenuFunction> menufunction = menuFunctions.stream().filter(w -> w.getFunctionId().equals(functionId)).findFirst();
                SysMenuFunction menuFunctionModel = new SysMenuFunction();
                menuFunctionModel.setId(menufunction.isPresent() ? menufunction.get().getId() : Tools.getUUID());
                menuFunctionModel.setFunctionId(functionId);
                menuFunctionModel.setMenuId(model.getId());
                this.sysMenuFunctionService.save(menuFunctionModel);
            }
        }

        return model.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(List<String> ids) {
        this.sysRoleMenuFunctionService
                .remove(new LambdaQueryWrapper<SysRoleMenuFunction>().in(SysRoleMenuFunction::getMenuId, ids));
        this.sysMenuFunctionService
                .remove(new LambdaQueryWrapper<SysMenuFunction>().in(SysMenuFunction::getMenuId, ids));
        this.removeByIds(ids);
    }

    /**
     * 根据 角色 id 获取 菜单列表
     *
     * @return
     */
    @Override
    public List<SysMenu> getMenusByRoleId() {
        List<SysMenu> allMenuList = this.lambdaQuery().eq(SysMenu::getIsShow, 1).orderByAsc(SysMenu::getNum).list();

        UserInfoVo userInfo = this.sysUserService.getUserInfo();

        if (userInfo.isAdministrator()) {
            return allMenuList;
        }

        List<String> roleIds = userInfo.getRoles();

        if (roleIds.isEmpty()) {
            return new ArrayList<>();
        }

        // 获取 菜单 集合
        List<SysMenu> oldMenuList = this.sysRoleMenuFunctionMapper.selectMenuByRoleId(roleIds);

        List<SysMenu> res = new ArrayList<>();

        for (SysMenu item : oldMenuList) {
            this.checkUpperLevel(allMenuList, oldMenuList, res, item);
            Boolean any = res.stream().anyMatch(w -> w != null && w.getId().equals(item.getId()));
            if (!any) {
                res.add(item);
            }
        }

        // 正序 排序
        res = res.stream().sorted(Comparator.comparing(SysMenu::getNum)).collect(Collectors.toList());

        return res;
    }

    /**
     * 辅助 函数 递归 查找 阶梯菜单
     *
     * @param allMenuList
     * @param oldMenuList
     * @param newMenuList
     * @param item
     */
    private void checkUpperLevel(List<SysMenu> allMenuList, List<SysMenu> oldMenuList, List<SysMenu> newMenuList, SysMenu item) {
        Boolean any_old = oldMenuList.stream().anyMatch(w -> w != null && w.getId().equals(item.getParentId()));
        Boolean any_new = newMenuList.stream().anyMatch(w -> w != null && w.getId().equals(item.getParentId()));
        if (!any_old && !any_new) {
            Optional<SysMenu> menu_first = allMenuList.stream().filter(w -> w.getId().equals(item.getParentId())).findFirst();

            if (menu_first.isPresent()) {
                SysMenu menu = menu_first.get();
                if (!ObjectUtils.isEmpty(menu)) {
                    newMenuList.add(menu);
                    this.checkUpperLevel(allMenuList, oldMenuList, newMenuList, menu);
                }
            }
        }
    }

    /**
     * 创建菜单 并将 菜单转换 Map 对象
     *
     * @param id
     * @param sysMenus
     * @return
     */
    @Override
    public List<Map<String, Object>> menusToListMap(String id, List<SysMenu> sysMenus) {

        List<Map<String, Object>> res = new ArrayList<>();
        List<SysMenu> sysMenuList;

        if (StringUtils.isEmpty(id)) {
            sysMenuList = sysMenus.stream().filter(w -> StringUtils.isEmpty(w.getParentId()))
                    .collect(Collectors.toList());
        } else {
            sysMenuList = sysMenus.stream().filter(w -> id.equals(w.getParentId())).collect(Collectors.toList());
        }

        boolean openTag = true;
        for (SysMenu item : sysMenuList) {
            List<Map<String, Object>> children = this.menusToListMap(item.getId(), sysMenus);
            Map<String, Object> map = new HashMap<>();
            map.put("id", item.getId());
            map.put("name", item.getName());
            map.put("path", item.getUrl());
            map.put("icon", item.getIcon());
            map.put("isClose", item.getIsClose());
            map.put("children", children);
            if (children.isEmpty() && openTag) {
                map.put("isOpen", 1);
                openTag = false;
            } else {
                map.put("isOpen", 0);
            }

            res.add(map);
        }

        return res;
    }

    /**
     * 根据菜单Id 获取 按钮权限
     *
     * @param id
     * @return
     */
    @Override
    public Map<String, Boolean> getPowerByMenuId(String id) {
        UserInfoVo userInfo = this.sysUserService.getUserInfo();
        SysMenu menu = this.baseMapper.selectById(id);
        List<SysFunction> functionList = this.sysFunctionService.lambdaQuery().orderByAsc(SysFunction::getNum).list();
        List<SysMenuFunction> menuFunctionList = this.sysMenuFunctionService.lambdaQuery().eq(SysMenuFunction::getMenuId, id).list();
        List<SysRoleMenuFunction> roleMenuFunctionList = this.sysRoleMenuFunctionService.lambdaQuery().in(SysRoleMenuFunction::getRoleId, userInfo.getRoles()).list();
        Map<String,Boolean> map = new HashMap<String, Boolean>();

        // 如果是超级管理员
        if (userInfo.isAdministrator()) {
            for (SysFunction item : functionList) {
                Boolean isPower = menuFunctionList.stream()
                        .anyMatch(w -> w.getMenuId().equals(id) && Objects.equals(w.getFunctionId(), item.getId()));
                if ("display".equals(item.getByName()) || menu.getParentId().equals(appConfigs.systemMenuId)) {
                    isPower = true;
                }
                map.put(item.getByName(), isPower);
            }
            return map;
        }

        for (SysFunction item : functionList) {
            if (!userInfo.getRoles().isEmpty()) {
                Boolean isPower = roleMenuFunctionList.stream()
                        .anyMatch(w -> w.getMenuId().equals(id) && w.getFunctionId().equals(item.getId()));
                map.put(item.getByName(), isPower);
            } else {
                map.put(item.getByName(), false);
            }
        }

        return map;
    }

    @Override
    public MenusFunctionTreeVo getMenusFunctionTree() {
        MenusFunctionTreeVo menusFunctionTree = new MenusFunctionTreeVo();
        List<SysMenu> allMenuList = this.lambdaQuery().orderByAsc(SysMenu::getNum).list();
        List<SysFunction> functionList = this.sysFunctionService.lambdaQuery().orderByAsc(SysFunction::getNum).list();
        List<SysMenuFunction> menuFunctionList = this.sysMenuFunctionService.lambdaQuery().orderByAsc(SysMenuFunction::getCreateTime).list();
        menusFunctionTree.setTree(this.createMenusFunctionTree(null, allMenuList, functionList, menuFunctionList, menusFunctionTree));
        return menusFunctionTree;
    }

    private List<TreeVo> createMenusFunctionTree(String id, List<SysMenu> aMenus, List<SysFunction> functions, List<SysMenuFunction> menuFunctions, MenusFunctionTreeVo menusFunctionTreeBo) {
        List menus = new ArrayList<TreeVo>();
        List<SysMenu> menuItems = null;
        if (StringUtils.isEmpty(id)) {
            menuItems = aMenus.stream().filter(w -> StringUtils.isEmpty(w.getParentId())).collect(Collectors.toList());
        } else {
            menuItems = aMenus.stream().filter(w -> id.equals(w.getParentId())).collect(Collectors.toList());
        }

        List<TreeVo> children;
        for (SysMenu sysMenu : menuItems) {
            children = new ArrayList<>();
            // 判断是否有子集
            if (aMenus.stream().anyMatch(w -> sysMenu.getId().equals(w.getParentId()))) {
                menusFunctionTreeBo.getDefaultExpandedKeys().add(sysMenu.getId());
                children = this.createMenusFunctionTree(sysMenu.getId(), aMenus, functions, menuFunctions,
                        menusFunctionTreeBo);
            } else {
                for (SysFunction function : functions) {
                    // 判断是否 该菜单下 是否勾选了 该功能
                    Boolean menuFunctionAny = menuFunctions.stream().anyMatch(
                            w -> w.getMenuId().equals(sysMenu.getId()) && w.getFunctionId().equals(function.getId()));
                    String unionId = sysMenu.getId() + function.getId();
                    if (menuFunctionAny) {
                        menusFunctionTreeBo.getDefaultCheckedKeys().add(unionId);
                    }
                    TreeVo treeBo = new TreeVo();
                    treeBo.setKey(unionId);
                    treeBo.setTitle(function.getName() + "-" + function.getByName());
                    treeBo.setDisabled(true);
                    children.add(treeBo);
                }
            }
            TreeVo treeBo = new TreeVo();
            treeBo.setKey(sysMenu.getId());
            treeBo.setTitle(sysMenu.getName() + "-" + sysMenu.getNum());
            treeBo.setDisabled(true);
            treeBo.setDisableCheckbox(true);
            treeBo.setChildren(children);
            menus.add(treeBo);
        }

        return menus;
    }

}
